package com.strato.datafactory.v1_0_0.loader;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import com.strato.annotation.osgi.v1_0_0.service.OsgiService;
import com.strato.datafactory.v1_0_0.api.DataDefine;
import com.strato.datafactory.v1_0_0.api.DataFilter;
import com.strato.datafactory.v1_0_0.filter.MapConditionSqlQueryFilter;
import com.strato.jdbc.datasource.v1_0_0.DataSourceFactory;
import com.strato.mvc.api.v1_0_0.user.UserContext;
import com.strato.orm.mybatis.v1_0_0.factory.SqlSessionFactoryProvider;
import com.strato.saas.merchant.v1_0_0.MerchantContextHolder;

@OsgiService
public class MybatisDataLoader extends Collection2MapDataLoaderAdaptor{
	
	public static final String DATA_SOURCE_KEY="dataSource";
	private static final String ATTR_MAPPER = "mapper";
	private static final String ATTR_METHOD = "method";

	public String getName(){
		return "mybatisDataLoader";
	}
	public Collection<Object> loadCollectionData(DataDefine dataDefine, List<DataFilter> filters){
		String dataSourceName=dataDefine.getAttribute(DATA_SOURCE_KEY);
		String mapperClassName=dataDefine.getAttribute(ATTR_MAPPER);
		String methodName=dataDefine.getAttribute(ATTR_METHOD);
		DataSource dataSource = DataSourceFactory.getInstance().getDataSource(dataSourceName);
		Connection conn=null;
		try {
			conn = dataSource.getConnection();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
		SqlSessionFactory sqlSessionFactory = SqlSessionFactoryProvider.getInstance().provide();
		SqlSession session = sqlSessionFactory.openSession(conn);
		try{
			Class<?> cls = Resources.getDefaultClassLoader().loadClass(mapperClassName);
			Object mapper = session.getMapper(cls);
			Map<String,Object> params=null;
			if(filters!=null&&!filters.isEmpty()){
				DataFilter dataFilter=filters.get(0);
				if(dataFilter instanceof MapConditionSqlQueryFilter){
					MapConditionSqlQueryFilter filter=(MapConditionSqlQueryFilter) filters.get(0);
					params=filter.getConditionValues();
				}				
			}
			return invokeMapperMethod(mapper,methodName,params);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e);
		}finally{
			try{
				session.close();
			}catch(Exception e){
				e.printStackTrace();
			}
			if(conn!=null){
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	public static final Class<?>[] MAPPER_PARAM_TYPE=new Class<?>[]{Map.class};
	
	
	@SuppressWarnings("unchecked")
	private Collection<Object> invokeMapperMethod(Object mapper, String methodName,Map<String,Object> param) {
		try {
			if(param==null){
				param=new HashMap<String,Object>();
			}
			String userId=UserContext.get();	
			param.put("userId", userId);
			String merchantId=MerchantContextHolder.get();
			param.put("merchantId", merchantId);
			Method method = mapper.getClass().getMethod(methodName, MAPPER_PARAM_TYPE);
			return (Collection<Object>) method.invoke(mapper, new Object[]{param});
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		} catch (SecurityException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		} catch (InvocationTargetException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
		
	}

}
